﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;

namespace LeanEngine.Extensions
{
    /// <summary>
    /// Extension methods for the data type System.String.
    /// </summary>
    public static class StringExtensions
    {
        #region Parse Extensions

        #region Default Culture

        /// <summary>
        /// Convert to System.Int16 according to the number format of culture set in the thread.
        /// </summary>        
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        public static Int16 ToInt16(this string value)
        {
            return Int16.Parse(value, CultureInfo.CurrentCulture.NumberFormat);
        }

        /// <summary>
        /// Convert to System.Int32 according to the number format of culture set in the thread.
        /// </summary>        
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        public static Int32 ToInt32(this string value)
        {
            return Int32.Parse(value, CultureInfo.CurrentCulture.NumberFormat);
        }

        /// <summary>
        /// Convert to System.Int64 according to the number format of culture set in the thread.
        /// </summary>        
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        public static Int64 ToInt64(this string value)
        {
            return Int64.Parse(value, CultureInfo.CurrentCulture.NumberFormat);
        }

        /// <summary>
        /// Convert to System.DateTime according to the number format of culture set in the thread.
        /// </summary>        
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <param name="value">The value to be converted.</param>
        public static DateTime ToDateTime(this string value)
        {
            return DateTime.Parse(value, CultureInfo.CurrentCulture.DateTimeFormat);
        }

        /// <summary>
        /// Convert to a Decimal type using the current culture-specific formating information
        /// </summary>
        public static decimal ToDecimal(this string value)
        {
            return decimal.Parse(value, CultureInfo.CurrentCulture.NumberFormat);
        }

        /// <summary>
        /// Convert to System.Boolean according to the number format of culture set in the thread.
        /// </summary>
        /// <exception cref="System.FormatException"></exception>
        /// <param name="value">The value to be converted.</param>
        public static bool ToBoolean(this string value)
        {
            return Convert.ToBoolean(value);
        } 

        #endregion

        #region Custom Culture

        /// <summary>
        /// Convert to System.Int16 according to the formatting information supplied.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        /// <param name="culture">An System.IFormatProvider that supplies culture-specific formatting information about s.</param>
        public static Int16 ToInt16(this string value, IFormatProvider culture)
        {
            return Int16.Parse(value, culture);
        }

        /// <summary>
        /// Convert to System.Int32 according to the formatting information supplied.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        /// <param name="culture">An System.IFormatProvider that supplies culture-specific formatting information about s.</param>
        public static Int32 ToInt32(this string value, IFormatProvider culture)
        {
            return Int32.Parse(value, culture);
        }

        /// <summary>
        /// Convert to System.Int64 according to the formatting information supplied.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        /// <param name="culture">An System.IFormatProvider that supplies culture-specific formatting information about s.</param>
        public static Int64 ToInt64(this string value, IFormatProvider culture)
        {
            return Int64.Parse(value, culture);
        }

        /// <summary>
        /// Convert to System.DateTime according to the formatting information supplied.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.FormatException"></exception>
        /// <exception cref="System.OverflowException"></exception>
        /// <param name="value">The value to be converted.</param>
        /// <param name="culture">An System.IFormatProvider that supplies culture-specific formatting information about s.</param>
        public static DateTime ToDateTime(this string value, IFormatProvider culture)
        {
            return DateTime.Parse(value, culture);
        }

        #endregion

        #endregion

        #region Validation Extensions

        #region Generic Extensions

        /// <summary>
        /// Evaluates whether the string not contains only numbers.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsNotNumeric(this string value)
        {
            return !IsNumeric(value);
        }

        /// <summary>
        /// Evaluates whether the string contains only numbers.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsNumeric(this string value)
        {
            return value.IsNullOrEmpty() ? false : Regex.IsMatch(value, @"^[-+]?[0-9]+([0-9]\.[0-9]+)?$");
        }

        /// <summary>
        /// Evaluates whether the string is null or empty.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsNullOrEmpty(this string value)
        {
            return string.IsNullOrEmpty(value);
        }

        /// <summary>
        /// Evaluates whether the string is not null or empty.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsNotNullOrEmpty(this string value)
        {
            return !string.IsNullOrEmpty(value);
        }

        /// <summary>
        /// Evaluates whether the string not matches the regular expression pattern provided.
        /// </summary>
        /// <param name="value">The current value.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        public static bool NotMatchPattern(this string value, string pattern)
        {
            return !Regex.IsMatch(value, pattern);
        }

        /// <summary>
        /// Evaluates whether the string matches the regular expression pattern provided.
        /// </summary>
        /// <param name="value">The current value.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        public static bool MatchPattern(this string value, string pattern)
        {
            return value.IsNullOrEmpty() ? false : Regex.IsMatch(value, pattern);
        }

        #endregion

        #region Specific Extensions

        /// <summary>
        /// Evaluates whether a string is not a valid email address.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsNotValidEmail(this string value)
        {
            return !value.IsValidEmail();
        }

        /// <summary>
        /// Evaluates whether a string is a valid email address.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsValidEmail(this string value)
        {
            return value.MatchPattern(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
        }

        /// <summary>
        /// Evaluates whether a string is not a valid cpf document number.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsNotValidCPF(this string value)
        {
            return !value.IsValidCpf();
        }

        /// <summary>
        /// Evaluates whether a string is a valid cpf document number.
        /// </summary>
        /// <param name="value">The current value.</param>
        public static bool IsValidCpf(this string value)
        {
            value.Remove("-");
            value.Remove(".");

            // Se for vazio
            if (value.IsNullOrEmpty() || IsRepeatedNumbers(value))
            {
                return false;
            }

            // Retirar todos os caracteres que não sejam numéricos
            string aux = string.Empty;
            for (int i = 0; i < value.Length; i++)
            {
                if (char.IsNumber(value[i]))
                {
                    aux += value[i].ToString();
                }
            }

            // O tamanho do CPF tem que ser 11
            if (aux.Length.NotEquals(11))
            {
                return false;
            }
            else
            {
                // Guardo o dígito para comparar no final
                string pDigito = aux.Substring(9, 2);
                aux = aux.Substring(0, 9);

                //Cálculo do 1o. digito do CPF
                aux += GetDigit(aux);

                //Cálculo do 2o. digito do CPF
                aux += GetDigit(aux);

                return (pDigito.Equals(aux.Substring(9, 2)));
            }
        }

        private static string GetDigit(string cpf)
        {
            int Peso = 2;
            int Soma = 0;

            for (int i = cpf.Length - 1; i >= 0; i--)
            {
                Soma += Peso * cpf[i].ToInt32();
                Peso++;
            }

            int pNumero = 11 - (Soma % 11);

            if (pNumero.IsGreaterThan(9))
            {
                pNumero = 0;
            }

            return pNumero.ToString();
        }


        private static bool IsRepeatedNumbers(string cpf)
        {
            string[] invalidValues = new string[] {"00000000000", "11111111111", 
                                                "22222222222", "33333333333", "44444444444", 
                                                "55555555555", "66666666666", "77777777777", 
                                                "88888888888", "99999999999"};
            for (int i = 0; i < invalidValues.Count(); i++)
            {
                if (cpf == invalidValues[i])
                {
                    return true;
                }
            }

            return false;
        }

        #endregion

        #endregion

        #region Other Extensions

        /// <summary>
        /// Replaces all occurrences of System.String in this instance, with an System.String.Empty.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.ArgumentException"></exception>
        /// <param name="source">The current value.</param>
        /// <param name="value">The string to be removed.</param>
        public static void Remove(this string source, string value)
        {
            source = source.Replace(value, string.Empty);
        }

        /// <summary>
        /// Returns an string array that contains the substrings in this instance that are delimited by elements
        /// of a specified delimiter value.
        /// </summary>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        /// <exception cref="System.ArgumentException"></exception>
        /// <param name="source">The object extended</param>
        /// <param name="delimiter">The string that delimit the substrings in this instance.</param>
        public static string[] Split(this string source, string delimiter)
        {
            return source.IsNullOrEmpty() ? new string[] { } : source.Split(delimiter.ToCharArray());
        }

        #endregion
    }
}
